package book.module2

import scala.annotation.tailrec

/**
 * 练习题解答
 */
object Text {
  //  def main(args: Array[String]): Unit = {
  //    //    println(fib(5))
  //    //    val arr = Array(1, 2, 4, 3)
  //    //    val bool = isSorted[Int](arr, _ - _ <= 0)
  //    //    println(bool)
  //    val value = drop[Int](List(1, 2, 3, 4), 2)
  //    println(value)
  //  }

  /**
   * P17  2.1
   */
  def fib(n: Int): Int = {
    if (n == 1) 0
    else if (n == 2 || n == 3) 1
    else fib(n - 1) + fib(n - 2)
  }

  /**
   * P20  2.2
   */
  def isSorted[A](as: Array[A], ordered: (A, A) => Boolean): Boolean = {
    @tailrec
    def loop(s: Int): Boolean = {
      if (s == as.length - 1) true
      else if (!ordered(as(s), as(s + 1))) false
      else loop(s + 1)
    }

    loop(0)
  }

  /**
   * P22  2.3 秒啊
   */
  def curry[A, B, C](f: (A, B) => C): A => (B => C)
  = (a: A) => (b: B) => f(a, b)

  /**
   * P22 2.4 妙啊
   */
  def uncurry[A, B, C](f: A => B => C): (A, B) => C
  = (a: A, b: B) => f(a)(b)

  /**
   * P22 2.5
   */
  def compose[A, B, C](f: B => C, g: A => B): A => C
  = (a: A) => f(g(a))

  /**
   * 3.2
   */
  def tail[A](l: List[A]): List[A] = l match {
    case Nil => Nil
    case _ :: sx => sx
  }

  /**
   * 3.3
   */
  def setHead[A](l: List[A], x: A): List[A] = l match {
    case Nil => x :: Nil
    case _ :: t => x :: t
  }

  /**
   * 3.4
   */
  @tailrec
  def drop[A](L: List[A], n: Int): List[A] = {
    if (n == 0) L
    else L match {
      case Nil => Nil
      case _ => drop(L.tail, n - 1)
    }
  }

  /**
   * 3.5
   */
  @tailrec
  def dropWhile[A](l: List[A], f: A => Boolean): List[A] = l match {
    case x :: xs if f(x) => dropWhile(xs, f)
    case _ => l
  }

  def dropWhileCurry[A](as: List[A])(f: A => Boolean): List[A] = as match {
    case x :: xs if f(x) => dropWhile(xs, f)
    case _ => as
  }

  /**
   * 3.6
   */
  def init[A](l: List[A]): List[A] = l match {
    case Nil => Nil
    case _ :: Nil => Nil
    case x :: xs => x :: init(xs)
  }

  def foldRight[A, B](as: List[A], z: B)(f: (A, B) => B): B = as match {
    case Nil => z
    case x :: xs => f(x, foldRight(xs, z)(f))
  }

  /**
   * 3.9
   */
  def length[A](as: List[A]): Int = foldRight(as, 0)((_, xs) => xs + 1)

  /**
   * 3.10
   */
  @tailrec
  def foldLeft[A, B](as: List[A], z: B)(f: (A, B) => B): B = as match {
    case Nil => z
    case x :: xs => foldLeft(xs, f(x, z))(f)
  }

  /**
   * 3.11
   */
  def sum2(l: List[Int]): Int = foldLeft(l, 0)(_ + _)

  def product2(l: List[Int]): Int = foldLeft(l, 1)(_ * _)

  /**
   * 3.12
   */
  def flip[A](l: List[A]): List[A] = foldLeft(l: List[A], Nil: List[A])((res, cur) => res :: cur)

  /**
   * 3.14
   */
  def append[A](as: List[A], a: List[A]): List[A] = foldLeft(as.reverse, a) { (x, xs) => x :: xs }

  /**
   * 3.16
   */
  def add1(l: List[Int]): List[Int] = {
    foldRight(l, Nil: List[Int])((h, t) => h + 1 :: t)
  }

  /**
   * 3.17
   */
  def doubleToString(l: List[Double]): List[String] = {
    foldRight(l, Nil: List[String])((h, t) => ::(h.toString, t))
  }

  def main(args: Array[String]): Unit = {

    val ints = Array(1, 2, 3)
    //  add1(ints)

  }


}
